﻿using JunionRobot;
using JunionRobotTaskManager.Repository;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SqlSugar;
using System.Text.Json.Serialization;
using JsonIgnoreAttribute = Newtonsoft.Json.JsonIgnoreAttribute;

namespace JunionRobotTaskManager.Models
{
    /// <summary>
    /// 机器人信息
    /// </summary>
    [SugarTable("DbRobot")]
    public class Robot : JnRobot
    {

        private static readonly object  _lock=new object();
        public Robot()
        {

        }




        #region Property
        [SugarColumn(IsIgnore = true)]
        public bool IsReconnecting { get; set; }
        /// <summary>
        /// 标定信息
        /// </summary>
        [SugarColumn(IsIgnore = true)]
        public CalibrationInfo CalibrationInfo { get; set; } = new CalibrationInfo();

        /// <summary>
        /// 机器人编号
        /// </summary>
        [SugarColumn(IsPrimaryKey = true)]
        public string Id { get; set; }

        /// <summary>
        /// 是否启用机器人
        /// </summary>
        public bool Enable { get; set; } = true;

        /// <summary>
        /// 机器人指定区域
        /// </summary>
        public string Region { get; set; }

        /// <summary>
        /// 机器人是否在重连
        /// </summary>
        [SugarColumn(IsIgnore = true)]
        public bool IsReconecting { get; set; }

        /// <summary>
        /// 机器人分配的任务Id
        /// </summary>
        [SugarColumn(IsNullable = true)]
        [JsonIgnore]
        public string JobId { get; set; }

        /// <summary>
        /// 机器人可以承载的物料型号
        /// </summary>
        [SugarColumn(ColumnDataType = "text", IsJson = true, IsNullable = true)]
        [JsonIgnore]
        public string[] Payloads { get; set; }


        private Job currentJob;
        /// <summary>
        /// 机器人当前的任务
        /// </summary>
        [SugarColumn(IsIgnore = true)]
        [JsonIgnore]
        public Job CurrentJob
        {
            get
            {
                return currentJob;
            }
            set
            {
                if (value == null)
                {
                    IdleTime = DateTime.Now;
                }
                else
                {
                    IdleTime = null;
                }
                currentJob = value;

            }


        }


        [SugarColumn(IsIgnore = true)]
        public DateTime? IdleTime { get; set; }

        public int Floor { get; set; }

        /// <summary>
        /// 是否在电梯中，最多同时存在一辆车呼叫电梯
        /// </summary>
        public bool InElevator { get; set; }
        #endregion

        #region Method

        public bool IsCharge()
        {
            return Mode?.ToLower().Contains("charge") ?? false || (Status?.ToLower().Contains("charging") ?? false);
        }


        //IsNeedCharge改为了不做电量判断，在调用前判断当前电量是否需要充电
        public Robot IsNeedCharge(IServiceScope scope, ILogger logger, List<Dock> docks, bool checkRegion)
        {
            lock(_lock)
            {
                //如果没有在充电,且有充电桩显示这个机器人在占用,要释放这个充电桩
                if (!IsCharge())
                {
                    var dockinfo = docks.Find(x => x.IsOccupied && x.RobotId == Id);
                    dockinfo?.ReSetDock(scope.ServiceProvider.GetService<IDockRepository>());
                }


                //然后进行判断是否要充电
                if (CurrentJob == null && Status.ToLower().Contains("idle") && docks.Any(x => !x.IsOccupied && ((checkRegion && x.Region == Region) || !checkRegion)))
                {
                    var idleDock = docks.Where(x=> !x.IsOccupied && ((checkRegion && x.Region == Region) || !checkRegion)).OrderByDescending(x => x.Floor == this.Floor).ThenBy(x => Math.Sqrt(Math.Pow(x.XPosition - PosX, 2) + Math.Pow(x.YPosition - PosY, 2))).First();  
                    var chargingJob = Job.GetChargeJob(Id, idleDock);

                    #region【2】分配充电任务
                    var route = this.Floor == idleDock.Floor ? $"{idleDock.Id}" : $"{this.Floor}F-{idleDock.Floor}F_{idleDock.Id}";
                    logger.LogInformation($"The robot (IP:{IP}) before excute charge route({route}),the status is:{Status} ");
                    if (RoutesWithConfirm(route, chargingJob.Id))
                    {

                        CurrentJob = chargingJob;
                        JobId = chargingJob.Id;
                        IJobRepoitory jobRepository = scope.ServiceProvider.GetService<IJobRepoitory>();
                        jobRepository.Add(chargingJob);
                        IDockRepository dockRepository = scope.ServiceProvider.GetService<IDockRepository>();
                        idleDock.IsOccupied = true;
                        idleDock.RobotId = Id;
                        dockRepository.UpdateEntity(idleDock);
                        logger.LogInformation($"++++++++++++++++++Find the available dock({idleDock.Id}).assign robot-{IP} to charge.+++++++++++++++++");
                        var robotRepository = scope.ServiceProvider.GetService<IRobotRepository>();
                        robotRepository.UpdateEntity(this);
                    }
                    else
                    {
                        logger.LogInformation($"The robot (IP:{IP}) excute charge route failed! ");
                    }



                    #endregion
                }



                return this;
            }
           

        }


        public Robot ResetPark(IServiceScope scope, List<Park> parks)
        {
            //f (CurrentJob!=null&& parks.Exists(x => x.IsOccupied && x.RobotId == Id))
            if (parks.Exists(x => x.IsOccupied && x.RobotId == Id))
            {
                var park = parks.First(x => x.IsOccupied && x.RobotId == Id);
                park.IsOccupied = false;
                park.RobotId = null;


                IParkRepository parkRepository = scope.ServiceProvider.GetService<IParkRepository>();
                parkRepository.UpdateEntity(park);


            }

            return this;
        }


        public bool BeginJob(Job job, IServiceScope scope, List<Dock> docks, ILogger _logger, string key = "a")
        {




            #region【1】如果占用充电桩则释放
            if (CurrentJob?.Type == TaskType.Charge)
            {
                var dock = docks.Find(x => x.Id == CurrentJob.Targets.Keys.First());
                CurrentJob.UpdateInfo(scope.ServiceProvider.GetService<IJobRepoitory>(), TaskState.Complete);
                dock.ReSetDock(scope.ServiceProvider.GetService<IDockRepository>());
                _logger.LogInformation($"IP:{IP} Assign new job(${job.ToString()}), reset the dock({dock.Id}) info");
            }



            #endregion

            #region【2】 更新数据任务信息
            var portRepository = scope.ServiceProvider.GetService<ITransferPortRepository>();
            var port = portRepository.Find(x => x.Id == job.CurrentTarget);
            var route = this.Floor == port.Floor ? $"{port.Id}" : $"{this.Floor}F-{port.Floor}F_{port.Id}";
            _logger.LogInformation($"The robot (IP:{IP}) before excute route {route},the status is:{Status} ");

            if (RoutesWithConfirm(route, job.Id, key))
            {
                IsAvailable = false;
                CurrentJob = job;
                IdleTime = null;
                _logger.LogInformation($"***************Robot excute the job {job.ToString()} success!********************");
                return job.AssignInfoRecord(scope, this);
            }
            else
            {
                _logger.LogInformation($"The robot (IP:{IP})  excute route {job.CurrentTarget} failed! ");
                return false;
            }


            #endregion
        }
        #endregion



        #region 重写Equal
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (ReferenceEquals(obj, this))
            {
                return true;
            }

            return obj is Robot && Id == (obj as Robot).Id;
        }

        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }


        #endregion


    }
}
